# Copyright (c) 2008-2009 Mikhail Chernov 
#
# This file is part of dcExplorer.
#
# dcExplorer is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# dcExplorer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with dcExplorer.  If not, see <http://www.gnu.org/licenses/>.

package dcExplorer::saver::IncrementalDB;
use strict;
use core::Empire;

use DBI;

sub new
{
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $self  = {};
	$self->{DB} = shift;
	$self->{Source} = shift;
	$self->{Debug} = undef;
	$self->{Debug} = shift;
	bless ($self, $class);
	return $self;
}

sub Diff
{
	my ($a, $b) = @_;
	return !((defined($a) && defined($b) && ($a eq $b)) || (!defined($a) && !defined($b)));
}

sub savePlayerTurn
{
	my $self = shift;
	my ($dbh, $sourceEmpire, $updateEmpire) = ($self->{DB}, $self->{Source}, shift);
	return if (defined($sourceEmpire->getProp('turn')) && !($sourceEmpire->getProp('turn') < $updateEmpire->getProp('turn')));
	my @params = qw(turn emperor taxe tehnology native_tehnology no_bonus_tech main_per_turn second_per_turn money_per_turn main_taxed second_taxed money_taxed main_per_turn_spent second_per_turn_spent money_per_turn_spent food_bonus homeworldx homeworldy governors_count penis_size vassal_money);
	my $sth = $dbh->prepare("insert into player_turn(reporter_id, ".join(", ", @params).
		") values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
	$sth->execute($updateEmpire->getProp('player_id'), map {$updateEmpire->getProp($_)} @params);
	$sth->finish();
}

sub savePlayerRace
{
	my $self = shift;
	my ($dbh, $sourceEmpire, $updateEmpire) = ($self->{DB}, $self->{Source}, shift);
	return if (defined($sourceEmpire->getProp('race_id')));
	my @params = qw(t_optimal t_delta race_nature industry_nature unused_resource bonus_multiply bonus_speed bonus_dr bonus_price bonus_iq bonus_build_peace bonus_build_war bonus_hp bonus_attack bonus_mining bonus_stealth bonus_detectors zanud spec nomads longlife intell old traiders small);
	my $sth = $dbh->prepare("insert into player_race(reporter_id, turn, player_id, race_id) values (?, ?, ?, ?)");
	$sth->execute(map {$updateEmpire->getProp($_)} qw(player_id turn player_id race_id));
	$sth->finish();
	$sth = $dbh->prepare("insert into race(id, reporter_id, turn, name, ".join(", ", @params).") values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
	@params = map {$updateEmpire->getProp($_)} qw(race_id player_id turn race_name t_optimal);
	push @params, $updateEmpire->getProp('t_delta') * 100;
	push @params, $_ foreach map {$updateEmpire->getProp($_)} qw(race_nature industry_nature unused_resource);
	push @params, $_ foreach map {$updateEmpire->getProp($_) * 10} qw(bonus_multiply bonus_speed bonus_dr bonus_price bonus_iq bonus_build_peace bonus_build_war bonus_hp bonus_attack bonus_mining bonus_stealth bonus_detectors);
	push @params, $_ foreach map {$updateEmpire->getProp($_)} qw(zanud spec nomads longlife intell old traiders small);
	$sth->execute(@params);
	$sth->finish();
}

sub savePlanets 
{
	my $self = shift;
	my ($dbh, $sourceEmpire, $updateEmpire) = ($self->{DB}, $self->{Source}, shift);
	my ($id, $turn) = map {$updateEmpire->getProp($_)} qw(player_id turn);
	my $stg = $dbh->prepare("insert into planet_geo(x, y, reporter_id, turn, o, e, m, t, s) values (?, ?, $id, $turn, ?, ?, ?, ?, ?)");
	my $sto = $dbh->prepare("insert into planet_owner(x, y, reporter_id, turn, owner_id) values (?, ?, $id, $turn, ?)");
	my $stj = $dbh->prepare("insert into planet_jumpable(x, y, reporter_id, turn, jumpable) values (?, ?, $id, $turn, ?)");
	my $sta = $dbh->prepare("insert into planet_age(x, y, reporter_id, turn, age) values (?, ?, $id, $turn, ?)");
	my $stn = $dbh->prepare("insert into planet_name(x, y, reporter_id, turn, name) values (?, ?, $id, $turn, ?)");

	for my $newPlanet ($updateEmpire->planets->getAllKnown)
	{
		my @coords = map {$newPlanet->getProp($_)} qw(x y);
		my $oldPlanet = $sourceEmpire->planets->get(@coords);
		my @geodata = map {$newPlanet->getProp($_)} qw(o e m t s);
		if (defined($newPlanet->getProp('s')) && !defined($oldPlanet->getProp('s')))
		{
			$stg->execute(@coords, @geodata) unless $self->{Debug};
			$stg->finish();
			printf STDERR "%4d:%-4d Geodata: %02d, %02d, %02d, %02d, %02d\n", @coords, @geodata;
		}
		my ($oldOwner, $newOwner) = ($oldPlanet->getProp('owner_id'), $newPlanet->getProp('owner_id'));
		if (Diff($oldOwner, $newOwner))
		{
			$sto->execute(@coords, $newOwner) unless $self->{Debug};
			$sto->finish();
			printf STDERR "%4d:%-4d Owner: %s (%d) -> %s (%d)\n", @coords,
				$sourceEmpire->players->get($oldOwner)->getProp('name'), $oldOwner,
				$updateEmpire->players->get($newOwner)->getProp('name'), $newOwner;
		}
		my ($oldJumpable, $newJumpable) = ($oldPlanet->getProp('jumpable'), $newPlanet->getProp('jumpable'));
		if (Diff($oldJumpable, $newJumpable))
		{
			$stj->execute(@coords, $newJumpable) unless $self->{Debug};
			$stj->finish();
			printf STDERR "%4d:%-4d Jumpable: %d -> %d\n", @coords, $oldJumpable, $newJumpable;
		}
		my ($oldName, $newName) = ($oldPlanet->getProp('name'), $newPlanet->getProp('name'));
		if (Diff($oldName, $newName))
		{
			printf STDERR "%4d:%-4d Name: %s -> %s\n", @coords, $oldName, $newName;
			$stn->execute(@coords, $newName) unless $self->{Debug};
			$stn->finish();
		}
		my ($oldAge, $newAge) = ($oldPlanet->getProp('age'), $newPlanet->getProp('age'));
		if (Diff($oldAge, $newAge))
		{
#			printf STDERR "%4d:%-4d Age: %d -> %d\n", @coords, $oldAge, $newAge;
			$sta->execute(@coords, $newAge) unless $self->{Debug};
			$sta->finish();
		}
		
	}
}

sub savePlayers
{
	my $self = shift;
	my ($dbh, $sourceEmpire, $updateEmpire) = ($self->{DB}, $self->{Source}, shift);
	my %tmp;
	$tmp{$_} = 1 for map{$_->getProp('player_id')} ($updateEmpire->players->getAllKnown, $sourceEmpire->players->getAllKnown);
	my @allPlayers = sort keys %tmp;

	my %descriptions = (
		undef => "Unknown",
		0 => "Enemy",
		1 => "Neutral",
		2 => "Allied",
		3 => "Vassal",
		4 => "Lord"
	);

	my ($id, $turn) = map {$updateEmpire->getProp($_)} qw(player_id turn);
	my $std = $dbh->prepare("insert into dip_relations(reporter_id, turn, player, type) values ($id, $turn, ?, ?)");
	my $stp = $dbh->prepare("insert into player_name(reporter_id, turn, player, name) values ($id, $turn, ?, ?)");
	for my $player_id (@allPlayers)
	{
		my ($oldRel, $newRel) = ($sourceEmpire->dipRelation($player_id), $updateEmpire->dipRelation($player_id));
		if (Diff($oldRel, $newRel))
		{
			$std->execute($player_id, $newRel) unless $self->{Debug};
			$std->finish();
			printf STDERR "Diplomacy: %d %s (%d) -> %s (%d)\n", $player_id,
				$descriptions{$oldRel}, $oldRel,
				$descriptions{$newRel}, $newRel;
		}
		my ($oldPlayer, $newPlayer) = ($sourceEmpire->players->get($player_id), $updateEmpire->players->get($player_id));
		my ($oldName, $newName) = ($oldPlayer->getProp('name'), $newPlayer->getProp('name'));
		if (defined($newName) && Diff($newName, $oldName))
		{
			$stp->execute($player_id, $newName) unless $self->{Debug};
			$stp->finish();
			printf STDERR "Player: %d <%s> -> <%s>\n", $player_id, $oldName, $newName;
		}
	}
}

sub saveEmpire
{
	savePlayerTurn(@_);
	savePlayerRace(@_);
	savePlanets(@_);
	savePlayers(@_);
}

1;
